Load Libraries
library(tidyverse)
library(tidycensus)
library(sf)
library(plotly)
library(crosstalk)
library(leaflet)
library(gganimate)
Get Census Data
nc_2016 <-
get_acs(geography = "county",
variables = "B01003_001",
state = "NC",
geometry = TRUE) %>%
mutate(year = "2016")
nc_2015 <-
get_acs(geography = "county",
variables = "B01003_001",
state = "NC",
year = 2015,
geometry = TRUE) %>%
mutate(year = "2015")
nc_2014 <-
get_acs(geography = "county",
variables = "B01003_001",
state = "NC",
year = 2014,
geometry = TRUE) %>%
mutate(year = "2014")
nc_2013 <-
get_acs(geography = "county",
variables = "B01003_001",
state = "NC",
year = 2013,
geometry = TRUE) %>%
mutate(year = "2013")
Bind Rows
big_table <- rbind(nc_2016, nc_2015, nc_2014, nc_2013)
glimpse(big_table)
Observations: 400
Variables: 7
$ GEOID [3m[38;5;246m<chr>[39m[23m "37001", "37003", "37005", "37007", "37009", "3701...
$ NAME [3m[38;5;246m<chr>[39m[23m "Alamance County, North Carolina", "Alexander Coun...
$ variable [3m[38;5;246m<chr>[39m[23m "B01003_001", "B01003_001", "B01003_001", "B01003_...
$ estimate [3m[38;5;246m<dbl>[39m[23m 157844, 37159, 10935, 25531, 26833, 17535, 47316, ...
$ moe [3m[38;5;246m<dbl>[39m[23m NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
$ year [3m[38;5;246m<chr>[39m[23m "2016", "2016", "2016", "2016", "2016", "2016", "2...
$ geometry [3m[38;5;246m<MULTIPOLYGON [°]>[39m[23m MULTIPOLYGON (((-79.54192 3..., MULT...
big_table2 <- big_table
st_geometry(big_table2) <- NULL
Test
first: ggplot facet_wrap
ggplot(big_table) +
geom_sf(aes(fill = estimate, color = estimate)) +
facet_wrap(~ year)

Second: side by side bargraph
big_table2 %>%
mutate(county = str_extract(NAME, "\\w+")) %>%
filter(county == "Mecklenburg" |
county == "Durham" |
county == "Guilford") %>%
select(county, estimate, year) %>%
gather("foo", "year", -county, -estimate) %>%
ggplot() +
geom_col(aes(x = county, y = estimate, fill = year),
position = "dodge") +
scale_fill_viridis_d() +
coord_flip()

Third: Simple ggplotly for one year (2016) but no slider.
ggnc <- ggplot(nc_2016) +
geom_sf(aes(fill = estimate, color = estimate))
ggplotly(ggnc)
fourth: crosstalk shared data & slider
shared_bigtable <- SharedData$new(big_table %>%
mutate(year = as.numeric(year)))
filter_slider("mapyear", "YEAR", shared_bigtable,
column=~year, timeFormat = "%Y")
GreenPalette <- colorNumeric(palette = "Greens",
domain = big_table$estimate)
big_table %>%
st_transform(crs = "+init=epsg:4326") %>%
leaflet(width = "100%") %>%
addProviderTiles(provider = "CartoDB.Positron") %>%
addPolygons(fillOpacity = 0.7,
smoothFactor = 0,
stroke = FALSE,
color = ~GreenPalette(estimate))
nc_share_leaf <- (shared_bigtable %>%
#st_transform(crs = "+init=epsg:4326") %>%
leaflet(width = "100%") %>%
addProviderTiles(provider = "CartoDB.Positron") %>%
addPolygons(fillOpacity = 0.7,
smoothFactor = 0,
stroke = FALSE,
color = ~GreenPalette(estimate)))
sf layer has inconsistent datum (+proj=longlat +datum=NAD83 +no_defs).
Need '+proj=longlat +datum=WGS84'
Fifth: annimate with gganimate
big3p
p
Frame 1 (1%)
Frame 2 (2%)
Frame 3 (3%)
Frame 4 (4%)
Frame 5 (5%)
Frame 6 (6%)
Frame 7 (7%)
Frame 8 (8%)
Frame 9 (9%)
Frame 10 (10%)
Frame 11 (11%)
Frame 12 (12%)
Frame 13 (13%)
Frame 14 (14%)
Frame 15 (15%)
Frame 16 (16%)
Frame 17 (17%)
Frame 18 (18%)
Frame 19 (19%)
Frame 20 (20%)
Frame 21 (21%)
Frame 22 (22%)
Frame 23 (23%)
Frame 24 (24%)
Frame 25 (25%)
Frame 26 (26%)
Frame 27 (27%)
Frame 28 (28%)
Frame 29 (29%)
Frame 30 (30%)
Frame 31 (31%)
Frame 32 (32%)
Frame 33 (33%)
Frame 34 (34%)
Frame 35 (35%)
Frame 36 (36%)
Frame 37 (37%)
Frame 38 (38%)
Frame 39 (39%)
Frame 40 (40%)
Frame 41 (41%)
Frame 42 (42%)
Frame 43 (43%)
Frame 44 (44%)
Frame 45 (45%)
Frame 46 (46%)
Frame 47 (47%)
Frame 48 (48%)
Frame 49 (49%)
Frame 50 (50%)
Frame 51 (51%)
Frame 52 (52%)
Frame 53 (53%)
Frame 54 (54%)
Frame 55 (55%)
Frame 56 (56%)
Frame 57 (57%)
Frame 58 (58%)
Frame 59 (59%)
Frame 60 (60%)
Frame 61 (61%)
Frame 62 (62%)
Frame 63 (63%)
Frame 64 (64%)
Frame 65 (65%)
Frame 66 (66%)
Frame 67 (67%)
Frame 68 (68%)
Frame 69 (69%)
Frame 70 (70%)
Frame 71 (71%)
Frame 72 (72%)
Frame 73 (73%)
Frame 74 (74%)
Frame 75 (75%)
Frame 76 (76%)
Frame 77 (77%)
Frame 78 (78%)
Frame 79 (79%)
Frame 80 (80%)
Frame 81 (81%)
Frame 82 (82%)
Frame 83 (83%)
Frame 84 (84%)
Frame 85 (85%)
Frame 86 (86%)
Frame 87 (87%)
Frame 88 (88%)
Frame 89 (89%)
Frame 90 (90%)
Frame 91 (91%)
Frame 92 (92%)
Frame 93 (93%)
Frame 94 (94%)
Frame 95 (95%)
Frame 96 (96%)
Frame 97 (97%)
Frame 98 (98%)
Frame 99 (99%)
Frame 100 (100%)
Finalizing encoding... done!
plot_13 <- ggplot(nc_2013) +
geom_sf(aes(fill = estimate, color = estimate))
plot_13

Sixth: foo
ggnc <- ggplot() +
geom_sf(data = nc_2016,
aes(fill = estimate, color = estimate)) +
geom_sf(data = nc_2013,
aes(fill = estimate, color = estimate))
ggnc
ggpp <- ggplotly(ggnc) %>%
add_trace(
z = ~estimate,
frame = ~year)
ggpp
animation_slider(ggpp)
ggplotly(ggnc, tooltip = "NAME") %>%
layout(
hovermode = "x",
margin = list(
t = 20,
b = 20,
l = 20,
r = 20),
)
Make Plotly Map
boarder_white <- list(color = toRGB("white"), width = 2)
usa_projection <- list(scope = "usa",
projection = list(type = "albers usa"),
showlakes = TRUE,
lakecolor = toRGB("white"))
plotlymap <- plot_geo(big_table, locationmode = 'county names') %>%
add_trace(
z = ~estimate, text = ~estimate, location = ~NAME,
color = ~estimate, colors = "Purples",
frame = ~NAME
) %>%
layout = usa_projection
plotlymap
ggplotly()
LS0tDQp0aXRsZTogIlBsb3RseSBDaG9yb3BsZXRoIg0KYXV0aG9yOiAiSm9obiBMaXR0bGUiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBMb2FkIExpYnJhcmllcw0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5Y2Vuc3VzKQ0KbGlicmFyeShzZikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShjcm9zc3RhbGspDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KGdnYW5pbWF0ZSkNCmBgYA0KDQoNCg0KIyMgR2V0IENlbnN1cyBEYXRhDQoNCmBgYHtyfQ0KbmNfMjAxNiA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDE2IikNCg0KbmNfMjAxNSA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIHllYXIgPSAyMDE1LA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDE1IikNCg0KbmNfMjAxNCA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIHllYXIgPSAyMDE0LA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDE0IikNCg0KbmNfMjAxMyA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgc3RhdGUgPSAiTkMiLA0KICAgICAgICAgIHllYXIgPSAyMDEzLA0KICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lIA0KICBtdXRhdGUoeWVhciA9ICIyMDEzIikNCmBgYA0KDQojIyBCaW5kIFJvd3MNCg0KYGBge3J9DQpiaWdfdGFibGUgPC0gcmJpbmQobmNfMjAxNiwgbmNfMjAxNSwgbmNfMjAxNCwgbmNfMjAxMykgDQpnbGltcHNlKGJpZ190YWJsZSkNCg0KYmlnX3RhYmxlMiA8LSBiaWdfdGFibGUNCnN0X2dlb21ldHJ5KGJpZ190YWJsZTIpIDwtIE5VTEwNCmBgYA0KDQoNCiMjIFRlc3QNCg0KZmlyc3Q6ICBnZ3Bsb3QgZmFjZXRfd3JhcA0KDQpgYGB7cn0NCmdncGxvdChiaWdfdGFibGUpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgKw0KICBmYWNldF93cmFwKH4geWVhcikNCmBgYA0KDQoNClNlY29uZDogIHNpZGUgYnkgc2lkZSBiYXJncmFwaA0KDQpgYGB7cn0NCmJpZ190YWJsZTIgJT4lIA0KICBtdXRhdGUoY291bnR5ID0gc3RyX2V4dHJhY3QoTkFNRSwgIlxcdysiKSkgJT4lIA0KICBmaWx0ZXIoY291bnR5ID09ICJNZWNrbGVuYnVyZyIgfCANCiAgICAgICAgICAgY291bnR5ID09ICJEdXJoYW0iIHwgDQogICAgICAgICAgIGNvdW50eSA9PSAiR3VpbGZvcmQiKSAlPiUgDQogIHNlbGVjdChjb3VudHksIGVzdGltYXRlLCB5ZWFyKSAlPiUgDQogIGdhdGhlcigiZm9vIiwgInllYXIiLCAtY291bnR5LCAtZXN0aW1hdGUpICU+JSANCiAgZ2dwbG90KCkgKw0KICBnZW9tX2NvbChhZXMoeCA9IGNvdW50eSwgeSA9IGVzdGltYXRlLCBmaWxsID0geWVhciksIA0KICAgICAgICAgICBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSArDQogIGNvb3JkX2ZsaXAoKSANCmBgYA0KDQpUaGlyZDogIFNpbXBsZSBnZ3Bsb3RseSBmb3Igb25lIHllYXIgKDIwMTYpIGJ1dCBubyBzbGlkZXIuDQoNCmBgYHtyfQ0KZ2duYyA8LSBnZ3Bsb3QobmNfMjAxNikgKw0KICBnZW9tX3NmKGFlcyhmaWxsID0gZXN0aW1hdGUsIGNvbG9yID0gZXN0aW1hdGUpKSANCg0KZ2dwbG90bHkoZ2duYykNCg0KDQpgYGANCg0KZm91cnRoOiAgY3Jvc3N0YWxrIHNoYXJlZCBkYXRhICYgc2xpZGVyDQoNCmBgYHtyfQ0Kc2hhcmVkX2JpZ3RhYmxlIDwtIFNoYXJlZERhdGEkbmV3KGJpZ190YWJsZSAlPiUgDQogIG11dGF0ZSh5ZWFyID0gYXMubnVtZXJpYyh5ZWFyKSkpDQpgYGANCg0KYGBge3J9DQpuY19zaGFyZV9zbGlkZSA8LSBmaWx0ZXJfc2xpZGVyKCJtYXB5ZWFyIiwgIllFQVIiLCBzaGFyZWRfYmlndGFibGUsIA0KICAgICAgICAgICAgICBjb2x1bW49fnllYXIsIHRpbWVGb3JtYXQgPSAiJVkiKQ0KYGBgDQoNCg0KYGBge3J9DQpHcmVlblBhbGV0dGUgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSAiR3JlZW5zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb21haW4gPSBiaWdfdGFibGUkZXN0aW1hdGUpDQoNCmJpZ190YWJsZSAlPiUgDQogIHN0X3RyYW5zZm9ybShjcnMgPSAiK2luaXQ9ZXBzZzo0MzI2IikgJT4lDQogIGxlYWZsZXQod2lkdGggPSAiMTAwJSIpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVyID0gIkNhcnRvREIuUG9zaXRyb24iKSAlPiUgDQogIGFkZFBvbHlnb25zKGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLA0KICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgIGNvbG9yID0gfkdyZWVuUGFsZXR0ZShlc3RpbWF0ZSkpDQpgYGANCg0KDQpgYGB7cn0NCkdyZWVuUGFsZXR0ZSA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJHcmVlbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IGJpZ190YWJsZSRlc3RpbWF0ZSkNCg0KbmNfc2hhcmVfbGVhZiA8LSAoc2hhcmVkX2JpZ3RhYmxlICU+JSANCiAgI3N0X3RyYW5zZm9ybShjcnMgPSAiK2luaXQ9ZXBzZzo0MzI2IikgJT4lDQogIGxlYWZsZXQod2lkdGggPSAiMTAwJSIpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVyID0gIkNhcnRvREIuUG9zaXRyb24iKSAlPiUgDQogIGFkZFBvbHlnb25zKGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLA0KICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgIGNvbG9yID0gfkdyZWVuUGFsZXR0ZShlc3RpbWF0ZSkpKQ0KYGBgDQoNCg0KYGBge3J9DQpic2NvbHMobmNfc2hhcmVfc2xpZGUsIG5jX3NoYXJlX2xlYWYpDQpgYGANCg0KRmlmdGg6ICBhbm5pbWF0ZSB3aXRoIGdnYW5pbWF0ZQ0KDQpgYGB7cn0NCmJpZzMgPC0gYmlnX3RhYmxlICU+JSBtdXRhdGUoeWVhciA9IGFzLm51bWVyaWMoeWVhcikpDQoNCmJpZzNwIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gYmlnMyAlPiUgZmlsdGVyKHllYXIgPT0gMjAxNiksIA0KICAgICAgICAgIGFlcyhmaWxsID0gZXN0aW1hdGUsIGNvbG9yID0gZXN0aW1hdGUpKSArDQogIGdlb21fc2YoZGF0YSA9IGJpZzMgJT4lIGZpbHRlcih5ZWFyID09IDIwMTMpLA0KICAgICAgICAgIGFlcyhmaWxsID0gZXN0aW1hdGUsIGNvbG9yID0gZXN0aW1hdGUpKSArDQogIGNvb3JkX3NmKGNycyA9IDQyNjksIGRhdHVtID0gTkEpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgdmlld196b29tX21hbnVhbCgxLCAxLCB4bWluID0gdmlld3MkeG1pbiwgeG1heCA9IHZpZXdzJHhtYXgsIA0KICAgICAgICAgICAgICAgICAgIHltaW4gPSB2aWV3cyR5bWluLCB5bWF4ID0gdmlld3MkeW1heCwgd3JhcCA9IFRSVUUpDQogICMgZmFjZXRfd3JhcCh+IHllYXIpICsNCiAgIyAgbGFicyh0aXRsZSA9ICdZZWFyOiB7ZnJhbWVfdGltZX0nKSArDQogICN0cmFuc2l0aW9uX3RpbWUoeWVhcikgKw0KICAjZWFzZV9hZXMoJ2xpbmVhcicpDQoNCmJpZzNwDQoNCmFuaW1hdGUoYmlnM3AsIDEwMCwgMTApDQpgYGANCg0KDQpgYGB7cn0NCmVhcnRoIDwtIHNmOjpzdF9hc19zZihybmF0dXJhbGVhcnRoOjpjb3VudHJpZXMxMTApDQp2aWV3cyA8LSBkYXRhLmZyYW1lKHJiaW5kKA0KICBzdF9iYm94KGVhcnRoW2VhcnRoJG5hbWUgPT0gJ0Rlbm1hcmsnLF0pLA0KICBzdF9iYm94KGVhcnRoW2VhcnRoJG5hbWUgPT0gJ0F1c3RyYWxpYScsXSkNCikpDQpwIDwtIGdncGxvdCgpICsgDQogIGdlb21fc2YoZGF0YSA9IGVhcnRoLCBmaWxsID0gJ3doaXRlJykgKyANCiAgZ2VvbV9zZihkYXRhID0gZWFydGhbZWFydGgkbmFtZSAlaW4lIGMoJ0Rlbm1hcmsnLCAnQXVzdHJhbGlhJyksXSwgZmlsbCA9ICdmb3Jlc3RncmVlbicpICsgDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoJ2xpZ2h0Ymx1ZScpKSArIA0KICB2aWV3X3pvb21fbWFudWFsKDEsIDEsIHhtaW4gPSB2aWV3cyR4bWluLCB4bWF4ID0gdmlld3MkeG1heCwgeW1pbiA9IHZpZXdzJHltaW4sIHltYXggPSB2aWV3cyR5bWF4LCB3cmFwID0gVFJVRSkNCmFuaW1hdGUocCwgMTAwLCAxMCkNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnBsb3RfMTMgPC0gZ2dwbG90KG5jXzIwMTMpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTQgPC0gZ2dwbG90KG5jXzIwMTQpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTUgPC0gZ2dwbG90KG5jXzIwMTUpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTYgPC0gZ2dwbG90KG5jXzIwMTYpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCnBsb3RfMTMNCmBgYA0KDQoNCg0KU2l4dGg6ICBmb28NCg0KYGBge3J9DQoNCmdnbmMgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBuY18yMDE2LCANCiAgICAgICAgICBhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgKw0KICBnZW9tX3NmKGRhdGEgPSBuY18yMDEzLCANCiAgICAgICAgICBhZXMoZmlsbCA9IGVzdGltYXRlLCBjb2xvciA9IGVzdGltYXRlKSkgDQoNCmdnbmMNCg0KDQoNCg0KZ2dwcCA8LSBnZ3Bsb3RseShnZ25jKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB6ID0gfmVzdGltYXRlLA0KICAgIGZyYW1lID0gfnllYXIpDQoNCmdncHANCg0KYW5pbWF0aW9uX3NsaWRlcihnZ3BwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmdncGxvdGx5KGdnbmMsIHRvb2x0aXAgPSAiTkFNRSIpICU+JSANCiAgbGF5b3V0KA0KICAgIGhvdmVybW9kZSA9ICJ4IiwNCiAgICBtYXJnaW4gPSBsaXN0KA0KICAgICAgdCA9IDIwLA0KICAgICAgYiA9IDIwLA0KICAgICAgbCA9IDIwLA0KICAgICAgciA9IDIwKSwNCiAgKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCiMjIENvdW50eSBMZXZlbCBQbG90bHkgDQoNCmh0dHBzOi8vbW9kZXJuZGF0YS5wbG90Lmx5L2NvdW50eS1sZXZlbC1jaG9yb3BsZXRoLWluLXBsb3RseS1hbmQtci8NCg0KYGBge3J9DQpjb3VudHlfZGYgPC0gbWFwX2RhdGEoImNvdW50eSIpIA0KbmNfY291bnR5IDwtIGNvdW50eV9kZiAlPiUgZmlsdGVyKHJlZ2lvbiA9PSAibm9ydGggY2Fyb2xpbmEiKQ0KDQogIA0Kc3RhdGVfZGYgPC0gbWFwX2RhdGEoInN0YXRlIikgDQpuY19kZiA8LSBzdGF0ZV9kZiAlPiUgZmlsdGVyKHJlZ2lvbiA9PSAibm9ydGggY2Fyb2xpbmEiKQ0KDQoNCmBgYA0KDQoNCiMjIE1ha2UgUGxvdGx5IE1hcA0KDQpgYGB7cn0NCmJvYXJkZXJfd2hpdGUgPC0gbGlzdChjb2xvciA9IHRvUkdCKCJ3aGl0ZSIpLCB3aWR0aCA9IDIpDQoNCnVzYV9wcm9qZWN0aW9uIDwtIGxpc3Qoc2NvcGUgPSAidXNhIiwgDQogICAgICAgICAgICAgICAgICAgICAgIHByb2plY3Rpb24gPSBsaXN0KHR5cGUgPSAiYWxiZXJzIHVzYSIpLA0KICAgICAgICAgICAgICAgICAgICAgICBzaG93bGFrZXMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWtlY29sb3IgPSB0b1JHQigid2hpdGUiKSkNCg0KcGxvdGx5bWFwIDwtIHBsb3RfZ2VvKGJpZ190YWJsZSwgbG9jYXRpb25tb2RlID0gJ2NvdW50eSBuYW1lcycpICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIHogPSB+ZXN0aW1hdGUsIHRleHQgPSB+ZXN0aW1hdGUsIGxvY2F0aW9uID0gfk5BTUUsDQogICAgY29sb3IgPSB+ZXN0aW1hdGUsIGNvbG9ycyA9ICJQdXJwbGVzIiwNCiAgICBmcmFtZSA9IH5OQU1FDQogICkgJT4lIA0KICBsYXlvdXQgPSB1c2FfcHJvamVjdGlvbg0KDQpwbG90bHltYXANCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90bHkoKQ0KYGBgDQoNCg0KDQo=